﻿package com.zouloux.medias 
{
	import com.zouloux.core.debug.Debug;
	import flash.events.AsyncErrorEvent;
	import flash.events.Event;
	import flash.events.IOErrorEvent;
	import flash.events.NetStatusEvent;
	import flash.media.SoundTransform;
	import flash.media.Video;
	import flash.net.NetConnection;
	import flash.net.NetStream;
	
	// Les events
	[Event(name="ready",  type="com.zouloux.medias.MediaEvent")]
	[Event(name="play",  type="com.zouloux.medias.MediaEvent")]
	[Event(name="stop",  type="com.zouloux.medias.MediaEvent")]
	[Event(name="bufferFull",  type="com.zouloux.medias.MediaEvent")]
	[Event(name="bufferEmpty",  type="com.zouloux.medias.MediaEvent")]
	[Event(name="notFound",  type="com.zouloux.medias.MediaEvent")]
	[Event(name="seek",  type="com.zouloux.medias.MediaEvent")]
	[Event(name="pause",  type="com.zouloux.medias.MediaEvent")]
	[Event(name="unpause",  type="com.zouloux.medias.MediaEvent")]
	[Event(name="cuePoint",  type="com.zouloux.medias.MediaEvent")]
	[Event(name="end",  type="com.zouloux.medias.MediaEvent")]
	
	/**
	 * ...
	 * @author ZoulouX
	 */
	public class VideoController extends AMediaController implements IMediaController
	{
		// La connection
		protected var _netConnection		:NetConnection;
		
		// Le stream
		protected var _netStream			:NetStream;
		
		// L'objet d'infos
		protected var _info					:VideoInfo;
		
		// Si la vidéo est prête
		protected var _ready				:Boolean			= false;
		
		
		// Les getters
		override public function get paused():Boolean
		{
			return _paused;
		}
		override public function get playing():Boolean
		{
			return _playing;
		}
		override public function get loop():uint
		{
			return _loop;
		}
		override public function get time():Number
		{
			if (_netStream != null)
				return _netStream.time;
			else return 0;
		}
		public function get stream ():NetStream
		{
			return _netStream;
		}
		override public function get volume():Number
		{
			return _netStream.soundTransform.volume;
		}
		public function get ready():Boolean
		{
			return _ready;
		}
		
		
		// Les setters
		override public function set paused (value:Boolean):void 
		{
			_paused = value;
			if (value)
				_netStream.pause();
			else
				_netStream.resume();
		}
		override public function set playing (value:Boolean):void 
		{
			_playing = value;
		}
		override public function set loop (value:uint):void 
		{
			_loop = value;
		}
		override public function set time(value:Number):void 
		{
			seek(value);
		}
		override public function set volume(value:Number):void
		{
			var soundTransform:SoundTransform = new SoundTransform(value);
			_netStream.soundTransform = soundTransform;
		}
		public function get info():VideoInfo
		{
			return _info;
		}
		public function set ready(value:Boolean):void 
		{
			_ready = value;
		}
		
		
		// Le constructeur du controller
		public function VideoController (pConnection:NetConnection = null)
		{
			Debug.core(this, "VideoController", arguments);
			
			// Exécuter le constructeur de la classe d'abstraction
			super();
			
			// Vérifier si on a reçu un netConnection
			if (pConnection != null)
			{
				// Enregistrer la netConnection passée en paramètres
				_netConnection = pConnection
			}
			else
			{
				// Créer une netConnection bidon
				_netConnection = new NetConnection();
				_netConnection.connect(null);
			}
			
			// Créer le netStream
			_netStream = new NetStream(_netConnection);
			_netStream.client = {
				onMetaData: function (pInfo:Object):void
				{
					// Créer l'objet info
					_info = new VideoInfo();
					
					// Le player est ready
					_ready = true;
					
					// Retranscrire les propriétés
					for (var i:* in pInfo)
					{
						_info[i] = pInfo[i];
					}
					
					// Dispatcher le ready event
					dispatchEvent(new MediaEvent(MediaEvent.READY));
				},
				onCuePoint: function (pCuePoint:Object):void
				{
					// Créer l'event
					var event:MediaEvent = new MediaEvent(MediaEvent.CUE_POINT);
					
					// Enregistrer l'objet
					event.time 			= pCuePoint.time;
					event.cuePoint 		= pCuePoint.parameters;
					event.cuePointName 	= pCuePoint.name;
					
					// Le dispatcher
					dispatchEvent(event);
				}
			}
		}
		
		// Chargement du fichier
		override public function load (pUrl:String, pAutoStart:Boolean = false):void
		{
			Debug.core(this, "load", arguments);
			
			// Charger
			_netStream.play(pUrl);
			
			// S'il n'y a pas d'autostart, on pause
			if (!pAutoStart)
			{
				_paused = true;
				_netStream.pause();
			}
			
			// Ecouter ce qu'il se passe sur le stream
			_netStream.addEventListener(NetStatusEvent.NET_STATUS, netStatusHandler);
			_netStream.addEventListener(AsyncErrorEvent.ASYNC_ERROR, asyncErrorHandler);
			_netStream.addEventListener(IOErrorEvent.IO_ERROR, ioErrorHandler);
		}
		
		// Lire
		override public function play (pAt:Number = -1, pLoop:uint = 0):void
		{
			Debug.core(this, "play", arguments);
			
			// Vérifier si on doit seeker
			if (pAt != -1)
				seek(pAt);
			
			// Enregistrer le nombre de boucles
			_loop = pLoop
			
			// Lire le stream
			_netStream.resume();
			
			// On est plus en pause
			_paused = false;
			_playing = true;
		}
		
		// Arrêter
		override public function stop ():void
		{
			Debug.core(this, "stop");
			
			// On remet au début
			_netStream.seek(0);
			
			// On pause
			_netStream.pause();
			_paused = true;
			_playing = false;
		}
		
		// Chercher à tel endroit
		override public function seek (pAt:Number):void
		{
			Debug.core(this, "seek", arguments);
			
			_netStream.seek(pAt);
		}
		
		// Fermer le flux
		override public function close (pCloseConnection:Boolean = true):void
		{
			Debug.core(this, "close", arguments);
			
			// On ferme la connection s'il le faut
			if (pCloseConnection == true)
				_netConnection.close();
			
			// On ferme le stream
			_netStream.close();
			
			// Et on efface les références
			_netConnection = null;
			_netStream = null;
		}
		
		// Attacher un objet vidéo
		public function attachVideo (pVideo:Video):void
		{
			pVideo.attachNetStream(_netStream);
		}
		
		
		// Changement de status de la vidéo
		protected function netStatusHandler (event:NetStatusEvent):void
		{
			trace("----- netStatusEvent");
			for (var i in event.info)
			{
				trace(i+": "+event.info[i]);
			}
			
			if (event.info.code == "NetStream.Play.Stop")
			{
				dispatchEvent(new MediaEvent(MediaEvent.END));
			}
		}
		
		// Erreur binaire sur le stream
		protected function ioErrorHandler(event:IOErrorEvent):void 
		{
			trace("----- ioErrorHandler");
			trace(event);
		}
		
		// Erreur asynchrone
		protected function asyncErrorHandler(event:AsyncErrorEvent):void 
		{
			trace("----- asyncErrorHandler");
			trace(event);
		}
	}
}